using System;
using System.Linq;
using System.Web.Mvc;
using TestBat.Frontend.Attributes;
using TestBat.Frontend.Controllers.ViewData.Test;
using TestBat.IModels.Domain;
using TestBat.IModels.Models;
using TestBat.Models.Exceptions;
using System.Collections.Generic;

namespace TestBat.Frontend.Controllers
{
    [Authorize]
    public class TestController : Controller
    {
        public TestController(IScheduleModel scheduleModel, ITestModel testModel, ITraineeModel traineeModel, ITestTaskModel testTaskModel)
        {
            this.scheduleModel = scheduleModel;
            this.testModel = testModel;
            this.traineeModel = traineeModel;
            this.testTaskModel = testTaskModel;
        }

        public ActionResult Introduction(Guid scheduleID)
        {
            // get schedule
            ISchedule schedule = scheduleModel.GetByID(scheduleID);

            if (schedule == null)
                throw new ArgumentException("Invalid argument scheduleID");

            IntroductionViewData vd = new IntroductionViewData(); // TODO: use DI
            vd.Schedule = schedule;

            return View(vd);
        }

        public ActionResult Test(Guid scheduleID)
        {
            // get schedule
            ISchedule schedule = scheduleModel.GetByID(scheduleID);
            if (schedule == null)
                throw new ArgumentException("Invalid argument scheduleID");

            // get trainee
            ITrainee trainee = traineeModel.GetByUsername(User.Identity.Name);

            // build test
            ITest test = testModel.Create(schedule, trainee);

            // TODO: check, if test is already finished

            // build viewdata
            TestViewData vd = new TestViewData(); // TODO: use DI
            vd.Test = test;

            return View(vd);
        }

        [AcceptAjax]
        public ActionResult SaveEssaySolution(Guid testTaskID, string answer)
        {
            _SaveEssaySolution(testTaskID, answer);

            return new EmptyResult();
        }

        [AcceptAjax]
        public ActionResult SaveMultipleChoiceSolution(Guid testTaskID, Guid choiceID)
        {
            _SaveMultipleChoiceSolution(testTaskID, choiceID);

            return new EmptyResult();
        }

        [AcceptAjax]
        public ActionResult SaveMultipleResponseSolution(Guid testTaskID, Guid[] choiceIDs)
        {
            _SaveMultipleResponseSolution(testTaskID, choiceIDs);

            return new EmptyResult();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Finish(FormCollection form)
        {
            // extract ID
            Guid testID = new Guid(form["testID"]);

            // get test
            ITest test = testModel.GetByID(testID);
            if (test == null)
                throw new ArgumentException("Invalid argument testID");

            if (!TestBelongsToAuthenticatedTrainee(testID))
                throw new InvalidOperationException("Permission to finish test denied");


            // save solutions
            foreach (var key in form.AllKeys)
            {
                var value = form[key];

                if (key.Contains('$'))
                { // HACK: there might be a better solution than to use type-checks
                    string[] tokens = key.Split('$');
                    string type = tokens[0];
                    Guid testTaskID = new Guid(tokens[1]);
                    if (type.Equals("EssaySolution")) // essay solution
                        _SaveEssaySolution(testTaskID, value);
                    else if (type.Equals("MultipleChoiceSolution"))
                        _SaveMultipleChoiceSolution(testTaskID, new Guid(value));
                    else if (type.Equals("MultipleResponseSolution"))
                    {
                        // parse value
                        IList<Guid> results = new List<Guid>();
                        string[] values = value.Split(',');
                        foreach (string v in values)
                        {
                            try
                            {
                                results.Add(new Guid(v));
                            }
                            catch { continue; }
                        }
                        
                        // save values
                        _SaveMultipleResponseSolution(testTaskID, results.ToArray<Guid>());

                    }
                    else

                        throw new ArgumentException("Invalid solution type");
                }
            }

            // try to finish test
            try
            {
                testModel.Finish(test);
            }
            catch (BusinessException)
            {
                // fail silently
            }

            FinishViewData vd = new FinishViewData(); // TODO: use DI
            return View(vd);
        }

        private void _SaveEssaySolution(Guid testTaskID, string answer)
        {
            if (!TestTaskBelongsToAuthenticatedTrainee(testTaskID))
                return; // fail silently

            try
            {
                testModel.SaveEssaySolution(testTaskID, answer);
            }
            catch (BusinessException)
            {
                // fail silently
            }
        }

        private void _SaveMultipleChoiceSolution(Guid testTaskID, Guid choiceID)
        {
            if (!TestTaskBelongsToAuthenticatedTrainee(testTaskID))
                return; // fail silently

            try
            {
                testModel.SaveMultipleChoiceSolution(testTaskID, choiceID);
            }
            catch (BusinessException)
            {
                // fail silently
            }
        }

        private void _SaveMultipleResponseSolution(Guid testTaskID, Guid[] choiceIDs)
        {
            if (!TestTaskBelongsToAuthenticatedTrainee(testTaskID))
                return; // fail silently

            try
            {
                testModel.SaveMultipleResponseSolution(testTaskID, choiceIDs);
            }
            catch (BusinessException)
            {
                // fail silently
            }
        }

        /// <summary>
        /// checks, if the trainee is the "owner" of this test-task
        /// </summary>
        /// <param name="testTaskID"></param>
        /// <returns></returns>
        private bool TestTaskBelongsToAuthenticatedTrainee(Guid testTaskID)
        {
            ITrainee trainee = traineeModel.GetByUsername(User.Identity.Name);

            ITestTask testTask = testTaskModel.GetByID(testTaskID);

            if (testTask == null || trainee == null)
                return false;

            if (testTask.Test.Trainee.Equals(trainee))
                return true;
            return false;
        }

        private bool TestBelongsToAuthenticatedTrainee(Guid testID)
        {
            ITrainee trainee = traineeModel.GetByUsername(User.Identity.Name);

            ITest test = testModel.GetByID(testID);

             if (test == null || trainee == null)
                return false;

             if (test.Trainee.Equals(trainee))
                 return true;
             return false;
        }

        private readonly IScheduleModel scheduleModel;
        private readonly ITestModel testModel;
        private readonly ITraineeModel traineeModel;
        private readonly ITestTaskModel testTaskModel;
    }
}
